﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FrameWorkBase
{
    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class CanSerialize : Attribute
    {
    }



    /// <summary>
    /// 参数类型
    /// </summary>
    internal class ParamInfo
    {
        public string CurrentType;
        public Type Type;
        public FieldInfo Info;

        public ParamInfo(string currentType,Type type)
        {
            this.CurrentType = currentType;
            this.Type = type;
        }

        public ParamInfo(string currentType,Type type,FieldInfo info)
        {
            this.CurrentType = currentType;
            this.Type = type;
            this.Info = info;
        }
    }

    public static class Serialization
    {
        readonly static public Dictionary<string, int> FixClasic = new Dictionary<string, int>()
        {
            {"Dictionary`2",1 },{"List`1",2 },{"Boolean",3 },{"Boolean[]",4 },{"Byte",5 },{"Byte[]",6 },{"SByte",7 },{"SByte[]",8 },{"UInt16",9 },{"UInt16[]",10 },{"Int16",11 },{"Int16[]",12 },
            {"UInt32",13 },{"UInt32[]",14 },{"Int32",15 },{"Int32[]",16 },{"UInt64",17 },{"UInt64[]",18 },{"Int64",19 },{"Int64[]",20 },{"Single",21 },{"Single[]",22 },{"Double",23 },{"Double[]",24 },
            {"String",25 },{"String[]",26 }
        };
        public static byte[] SerializationData(object data)
        {
            Type type = data.GetType();

            //基础类型直接序列化
            if (FixClasic.ContainsKey(type.Name))
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    BinaryWriter binary = new BinaryWriter(memoryStream);
                    AddBuff(new ParamInfo(type.Name, type), data, ref binary);
                    return SplitData(ref binary);
                }
            }


            //MethodInfo[] methods = type.GetMethods();
            //PropertyInfo[] properties = type.GetProperties();
            FieldInfo[] fieldInfos = type.GetFields();
            List<FieldInfo> list = new List<FieldInfo>();

            foreach (var item in fieldInfos)
            {
                CanSerialize? de = item.GetCustomAttribute<CanSerialize>();
                if (de != null)
                {
                    list.Add(item);
                }
            }
            return ConvertObj(list, data);
        }

        static readonly char[] NullChar = new char[] { '*', 'n', 'i', 'l', '*' };
        static byte[] ConvertObj(List<FieldInfo> list, object data)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                BinaryWriter binary = new BinaryWriter(memoryStream);

                foreach (var item in list)
                {
                    object value = item.GetValue(data);
                    if (value != null)
                    {
                        ParamInfo info = new ParamInfo(item.FieldType.Name, item.FieldType);
                        AddBuff(info, value, ref binary);
                    }
                    else
                    {
                        binary.Write(NullChar);//传出空
                    }
                }
                return SplitData(ref binary);//裁剪
            }
        }

        /// <summary>
        /// 裁剪
        /// </summary>
        static byte[] SplitData(ref BinaryWriter binary)
        {
            int length = (int)binary.BaseStream.Length;
            byte[] buff = new byte[length];
            Array.Copy((binary.BaseStream as MemoryStream).GetBuffer(), 0, buff, 0, length);
            return buff;
        }

        static void AddBuff(ParamInfo info,object value, ref BinaryWriter binary)
        {
            switch (info.CurrentType)
            {
                case "Dictionary`2":
                    {
                        Type[] types = info.Type.GetGenericArguments();// ().FieldType.GetGenericArguments();
                        string KeyName = types[0].Name;
                        string ValueName = types[1].Name;
                        IDictionary dic = value as IDictionary;
                        binary.Write(dic.Count);

                        ICollection keys = dic.Keys;
                        Array keyArray = Array.CreateInstance(types[0], keys.Count);  
                        keys.CopyTo(keyArray, 0);

                        ICollection values = dic.Values;
                        Array valueArray = Array.CreateInstance(types[1], values.Count);
                        values.CopyTo(valueArray, 0);

                        for (int i = 0; i < dic.Count; i++)
                        {
                            //key
                            byte[] keyRes = SerializationData(keyArray.GetValue(i));
                            binary.Write(keyRes);
                            //value
                            byte[] valueRes = SerializationData(valueArray.GetValue(i));
                            binary.Write(valueRes);
                        }
                    }
                    break;
                case "List`1":
                    {
                        Type[] types = info.Type.GetGenericArguments();
                        string ArgName = types[0].Name;
                        IList ls = value as IList;
                        binary.Write(ls.Count);
                        for (int i = 0; i < ls.Count; i++)
                        {
                            byte[] result = SerializationData(ls[i]);
                            binary.Write(result);
                        }
                    }
                    break;
                case "Boolean":
                    {
                        binary.Write((bool)value);
                    }
                    break;
                case "Boolean[]":
                    {
                        bool[] array = (bool[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Byte":
                    {
                        binary.Write((byte)value);
                    }
                    break;
                case "Byte[]":
                    {
                        byte[] array = (byte[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "SByte":
                    {
                        binary.Write((sbyte)value);
                    }
                    break;
                case "SByte[]":
                    {
                        sbyte[] array = (sbyte[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "UInt16":
                    {
                        binary.Write((ushort)value);
                    }
                    break;
                case "UInt16[]":
                    {
                        ushort[] array = (ushort[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Int16":
                    {
                        binary.Write((short)value);
                    }
                    break;
                case "Int16[]":
                    {
                        short[] array = (short[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "UInt32":
                    {
                        binary.Write((uint)value);
                    }
                    break;
                case "UInt32[]":
                    {
                        uint[] array = (uint[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Int32":
                    {
                        binary.Write((int)value);
                    }
                    break;
                case "Int32[]":
                    {
                        int[] array = (int[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "UInt64":
                    {
                        binary.Write((ulong)value);
                    }
                    break;
                case "UInt64[]":
                    {
                        ulong[] array = (ulong[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Int64":
                    {
                         binary.Write((long)value);
                    }
                    break;
                case "Int64[]":
                    {
                        long[] array = (long[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Single":
                    {
                        binary.Write((float)value);
                    }
                    break;
                case "Single[]":
                    {
                        float[] array = (float[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "Double":
                    {
                        binary.Write( (double)value);
                    }
                    break;
                case "Double[]":
                    {
                        double[] array = (double[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item);
                        }
                    }
                    break;
                case "String":
                    {
                        string v = value.ToString();
                        binary.Write(v.Length);
                        binary.Write(Encoding.UTF8.GetBytes(v));
                    }
                    break;
                case "String[]":
                    {
                        string[] array = (string[])value;
                        binary.Write(array.Length);
                        foreach (var item in array)
                        {
                            binary.Write(item.Length);
                            binary.Write(Encoding.UTF8.GetBytes(item));
                        }
                    }
                    break;
                default:
                    {
                        binary.Write(SerializationData(value));
                    }
                    break;
            }
        }






    }
}
